#!/bin/bash

# 返回码
declare -i ret_ok=0                                       # 成功
declare -i ret_failed_file_not_exist=2                    # 失败 输入文件不存在
declare -i ret_failed_signature_verify_failed=3           # 失败 证书的签名文件校验失败 或者证书格式非法无效或者证书内容被篡改
declare -i ret_failed_certificate_expired=4               # 失败 证书已过期
declare -i ret_failed_extensions_no_CA_Flag=5             # 失败 找不到扩展域基本限制中的CA标志
declare -i ret_failed_cert_invalid=6                      # 失败 证书不能加载
declare -i ret_failed_extensions_no_Certifacate_Signing=7 # 失败 找不到扩展域中证书签名字段
declare -i ret_failed_no_CommonName=8                     # 失败 找不到CommonName字段
declare -i ret_ok_certificate_expiring=9                  # 成功 证书快过期

#-------------------------------------------------------
#  命令执行 ./check_FD_cert.sh root_ca.crt
#  返回值:成功 返回值为0 失败返回值为1
#  信息打印：
#  格式为 check_FD_cert-Ret:[OK/Failed] info:[xxx]
#  Ret：后面打印是否成功 info提示具体的错误信息。当成功时，
#  info打印的是需要传出的一些信息
#  失败格式如下
#   check_FD_cert:Failed info:file not exist
#  成功格式如下
#   check_FD_cert-Ret:OK info:
#------------------------------------------------------

prefix="check_FD_cert"

#证书签名校验生成的临时文件 调用后会删除
CERTIFACATE_PUBKEY_OUTPUT="./public-key.pem"
SIGNATURE_OUTPUT="./x509-signature.bin"
BODY_OUTPUT="./x509-body.bin"

#打印到控制台信息
function note()
{
    ret=$1
    info=$2
    echo $prefix-"Ret:"$ret "info:"$info
}

debug="0"

function debug()
{
    if [ $debug == "1" ]
    then
        echo -e "Debug: "$1:$2
    fi
}

#提取证书 获取证书算法
function Certifacate_Extract_Signature(){
    #提取证书签名信息
    Signature=`openssl x509 -in $1 -text -noout | grep  "Signature Algorithm" | sed -n '1p' | awk '{print $3}'`
    lenth_limit=2048
    if [ -z "$Signature" ];then
        debug "get Signature algorithm failed"
        return 1
    fi
    if [[ $Signature =~ "ecdsa" ]];then
        Signature_Length=`openssl x509 -in $1 -text -noout | grep  Public-Key | awk '{print $2}'| sed 's/(//g' `
        lenth_limit=256
    elif [[ $Signature =~ "RSA" ]];then
        if [ -e /etc/redhat-release ] && [ ! -e /etc/lsb-release ]; then
            Signature_Length=`openssl x509 -in $1 -text -noout | grep  Public-Key | awk '{print $3}'| sed 's/(//g'`
        else
            Signature_Length=`openssl x509 -in $1 -text -noout | grep  Public-Key | awk '{print $2}' | sed 's/(//g'`
        fi
    fi
    if [ -z "$Signature_Length" ];then
        debug "Failed:get Signature algorithm"
        return 1
    fi
}

function check_Certifacate_Extract_Signature(){
    Signature_info=""
    if [[ $Signature =~ "sha256WithRSAEncryption" || $Signature =~ "ecdsa-with-SHA256"  ]];then
        Signature_info="Signature success"
    else
        Signature_info="Signature invalid"
        return 1
    fi
    if [ $Signature_Length -lt $lenth_limit ];then
        Signature_info="Signature length invalid"
        return 1
    fi
}

#对证书进行签名校验的入口函数
function Verify_Certifacate_sinature(){
    #提取证书中签名信息 二进制格式
    Certifacate_Extract_Signature $1;
    if [ $? == 1 ];then
        debug "Verify signature Extract signature failed"
        return 1
    fi
    check_Certifacate_Extract_Signature
    if [ $? != 0 ];then
        debug "check_Certifacate_Extract_Signature failed"
        return 1
    fi
}

function Get_Cert_Info_cmd(){
    #输出证书中的subject
    subject=`openssl x509 -in $1 -subject | grep subject | grep "\/"`
    if [ ! -z "$subject" ];then
        subject=`openssl x509 -in $1 -subject | grep subject | awk '{$1=""; print $0}' | sed 's|\/| |g' | sed 's/^[ \t]*//g'`
        issuer=`openssl x509 -in $file -issuer | grep issuer | awk '{$1=""; print $0}' | sed 's|\/| |g' | sed 's/^[ \t]*//g'`
        #获取subject中CN信息
        CN=$(openssl x509 -in $1 -noout -subject | grep "CN="  | sed 's/^.*CN=//g' | sed 's/, emailAddress.*$//g')
        if [ -z "$CN" ];then
            note "Failed" "subject CN not find"
            return ${ret_failed_no_CommonName}
        fi
    else
        subject=`openssl x509 -in $1 -subject | grep subject | awk '{$1="C"; print $0}'`
        issuer=`openssl x509 -in $file -issuer | grep issuer | awk '{$1="C"; print $0}'`
        CN=$(openssl x509 -in $1 -noout -subject | grep "CN = "  | sed 's/^.*CN = //g' | sed 's/, emailAddress.*$//g')
        if [ -z "$CN" ];then
            note "Failed" "subject CN not find"
            return ${ret_failed_no_CommonName}
        fi
    fi
}

# 证书过期校验
function Get_Cert_Time(){
    remain_time=$1
    #判断有效时间 如果小于0 则失效
    if [ $remain_time -lt 0 ];then
        return 1
    fi
    # 判断有效时间 如果小于90 则提示
    if [ $remain_time -ge 0 ] && [ $remain_time -le 90 ];then
        return 2
    fi
    return 0
}

function main()
{
    #参数校验 判断输入文件是否存在
    if [ ! -f $1 ];then
        note "Failed" "file not exist"
        return ${ret_failed_file_not_exist}
    fi

    file=$1

    openssl x509 -in $file -noout
    if [ $? != 0 ];then
        note "Failed" "Cert invalid"
        return ${ret_failed_cert_invalid}
    fi

    #判断证书签名是否合法,使用签名 公钥和源数据进行测试
    Verify_Certifacate_sinature $file

    
    #获取当前时间
    cur_time=`date +%s`
    #获取脚本失效时间
    end_time=`openssl x509 -in $file -enddate | grep notAfter | awk -F '=' '{print $2}' | xargs -i  date -d {}  +%s`
    start_time=`openssl x509 -in $file -startdate | grep notBefore | awk -F '=' '{print $2}' | xargs -i  date -d {}  +%s`

    #比较有效时间 天为单位
    diff_time=$[$[$end_time-$cur_time]/60/60/24]
    if [ "${time}" == "time" ];then
        Get_Cert_Time $diff_time
        res=$?
        if [ $res == 1 ];then
            note "Failed" "Validity out of date"
            return ${ret_failed_certificate_expired}
        elif [ $res == 2 ];then
            note "certificate Expiring" "$diff_time"
            return ${ret_ok_certificate_expiring}
        else
            note "ok" "$diff_time"
            return ${ret_ok}
        fi
    fi
    #判断有效时间 如果小于0 则失效
    if [ $diff_time -le 0 ];then
        note "Failed" "Validity out of date"
        return ${ret_failed_certificate_expired}
    fi

    #获取序列号
    serial=`openssl x509 -in $file -serial | grep serial | awk -F '=' '{print $2}'`

    #Get_Cert_Time $file
    if [ $? == 1 ];then
        note "Failed" "certificate expired"
        return ${ret_failed_certificate_expired}
    fi

    Get_Cert_Info_cmd $file

    #判断CA:True标志是否存在
    CAFlag=`openssl x509 -in $1 -noout -text -extensions basicConstraints | grep CA: | sed 's/^.*CA://g' | sed 's/[, ].*$//'`
    debug "CAFlag" "$CAFlag"
    if [ "$CAFlag" != "TRUE" ];then
        note "Failed" "basicConstraints CA True not find"
        return ${ret_failed_extensions_no_CA_Flag}
    fi

    #判断KeyUsage是否包含Certificate Sign字段
    keyUsage=`openssl x509 -in $1 -noout -text -extensions keyUsage | grep "Certificate Sign"`
    debug "keyUsage:" "$keyUsage"
    if [ -z "$keyUsage" ];then
        note "Failed" "keyUsage Certificate Sign not find"
        return ${ret_failed_extensions_no_Certifacate_Signing}
    fi

    #生成指纹信息
    fingerAlg="sha1"
    fingerprint=`openssl  x509 -in $1  -$fingerAlg -fingerprint | grep Fingerprint= | sed 's/^.*Fingerprint=//g'`

    #校验成功 增加传出信息
    info="sernum:$serial--starttime:$start_time--endtime:$end_time--subject:$subject--issuer:$issuer--sinature:$Signature_info"

    note "OK" "$info"
    return ${ret_ok}
}

csplit -z $1 '/-----BEGIN CERTIFICATE-----/' {*} -b "%02d.crt" -f root_ca > /dev/null 2>&1
time=$2

for file in $(ls root_ca*); do
    output=$(main "$file")
    RESULT=$?
    rm -f $file
    if [ "$RESULT" -ne 0 ]; then
        break
    fi
done
echo $output
exit $RESULT

